/*************************************************************************
	> File Name: signal.c
	> Author: Thinking
	> Mail: program_code@sohu.com 
	> Created Time: Wed 16 Jun 2021 05:45:46 AM PDT
 ************************************************************************/

#include <linux/kernel.h>			//Need for loglevels (KERN_WARNING KERN_INFO, KERN_EMERG)

#include <linux/module.h>			//Needed for all kernels modules
#include <linux/moduleparam.h>		

#include <linux/cdev.h>
#include <linux/fs.h>				//filp_open 
#include <linux/wait.h>				
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>				//kmalloc

#include <linux/unistd.h>			//sys_call_table
#include <linux/syscalls.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/fdtable.h>
#include <linux/uaccess.h>
#include <linux/rtc.h>

#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/time.h>

#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/cdev.h>

#include <asm/siginfo.h>
#include <linux/pid.h>
//#include <linux/sched/signal.h>
#include <linux/pid_namespace.h>


#define MYGPIO_HW_ENABLE

#define SIGNAL_ENABLE

#define MYGPIO_NAME "mygpio"

#define MYGPIO_NUMBER 4

static struct class *gpio_class;

struct cdev gpio_cdev[MYGPIO_NUMBER];

static int g_pid = 0;
int gpio_major = 0;
int gpio_minor = 0;

static void gpio_hw_init(int gpio)
{
	printk("gpio_hw_init is called: %d. \n", gpio);
}

static void gpio_hw_release(int gpio)
{
	printk("gpio_hw_release is called: %d. \n", gpio);
}

static void gpio_hw_set(unsigned long gpio_no, unsigned int val)
{
	printk("gpio_hw_set is called: gpio_no = %lu, val = %u \n", gpio_no, val);
}

static int gpio_open(struct inode* inode, struct file* file)
{
	return 0;
}

#ifdef SIGNAL_ENABLE

static void send_signal(int sig_no)
{
	int ret = 0;

	struct siginfo info;
	
	struct task_struct* task = NULL;


	if (0 == g_pid)
	{
		printk("pid[%d] is no valid! \n", g_pid);
		return;
	}

	memset(&info, 0, sizeof(struct siginfo));
	info.si_signo = sig_no;
	info.si_errno = 100;
	info.si_code = 200;


	rcu_read_lock();

	task = pid_task(find_vpid(g_pid), PIDTYPE_PID);

	rcu_read_unlock();

	if (task == NULL)
	{
		printk("get pid_task faild! \n");
		return;
	}

	ret = send_sig_info(sig_no, &info, task);


	if (ret < 0)
	{
		printk("send signal faild! \n");
	}
		
}

static long gpio_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
	
	void __user *pArg;

	printk("gpio_ioctl is called. cmd = %d \n", cmd);


	if (100 == cmd) 
	{
		pArg = (void*)arg;
		
		if(!access_ok(VERIFY_READ, pArg, sizeof(int)))
		{
			printk("access faild! \n");
			return -EACCES;
		}

		if (copy_from_user(&g_pid, pArg, sizeof(int)))
		{
			printk("copy_form_user faild \n");
			return -EACCES;
		}

		printk("save g_pid success : %d\n", g_pid);

		if (g_pid > 0)
		{
			send_signal(SIGUSR1);
			send_signal(SIGUSR2);
		}

	}
	return 0;
}
#else
static long gpio_ioctl(struct file* file, unsigned int val, unsigned long gpio_no)
{
	printk("gpio_ioctl is called .\n");

	if (0 != val && 1 != val)
	{
		printk("val is NOT valid !\n");
		return 0;
	}


	if (gpio_no >= MYGPIO_NUMBER)
	{
		printk("dev_no is invalid! \n");
		return 0;
	}


	gpio_hw_set(gpio_no, val);

	return 0;
}
#endif



static const struct file_operations gpio_ops = {
	.owner = THIS_MODULE,
	.open = gpio_open,
	.unlocked_ioctl = gpio_ioctl
};

static int __init signal_init(void)
{
	int i = 0;
	int devno = 0;
	dev_t num_dev;

	printk("signal_init_function\n");
	alloc_chrdev_region(&num_dev, gpio_minor, MYGPIO_NUMBER, MYGPIO_NAME);
	
	gpio_major = MAJOR(num_dev);

	printk("gpio_major = %d.\n", gpio_major);
	
	gpio_class = class_create(THIS_MODULE, MYGPIO_NAME);

	for (i = 0; i < MYGPIO_NUMBER; i++)
	{
		devno = MKDEV(gpio_major, gpio_minor + i);

		cdev_init(&gpio_cdev[i], &gpio_ops);
		
		cdev_add(&gpio_cdev[i], devno, 1);

		device_create(gpio_class , NULL, devno, NULL, MYGPIO_NAME"%d", i);

	}

	for (i = 0; i < MYGPIO_NUMBER; i++)
	{
		gpio_hw_init(i);
	}

	return 0;
}

void __exit signal_exit(void)
{

	int i;

	printk("gpio_driver_exit is called. \n");

	for (i = 0; i < MYGPIO_NUMBER; i++)
	{
		cdev_del(&gpio_cdev[i]);
		device_destroy(gpio_class, MKDEV(gpio_major, gpio_minor + i));
	}

	class_destroy(gpio_class);

	for (i = 0; i < MYGPIO_NUMBER; i++)
	{
		gpio_hw_release(i);
	}

	unregister_chrdev_region(MKDEV(gpio_major, gpio_minor), MYGPIO_NUMBER);


    return;
}
module_init(signal_init);
module_exit(signal_exit);
MODULE_LICENSE("GPL");
